package org.llc.common.starter.handle;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.llc.common.exception.BusinessCommonException;
import org.llc.common.starter.mapper.MenuMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import org.llc.common.starter.constants.EnableDynamicAutEnum;
import org.llc.common.starter.constants.Oauth2ExceptionEnum;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 从数据库中获取url对应的role信息
 *
 * @author llc
 * @date 2020/1/13 16:57
 * @since 1.0.0
 */
@Slf4j
@ConditionalOnExpression("${security.oauth2.token-store.enable-dynamic-auth:true} || ${security.oauth2.token-store.enable-dynamic-scope-auth:true}")
public class UrlRolesFilterHandler implements FilterInvocationSecurityMetadataSource {

    /**
     * 动态路由默认关闭
     */
    private boolean enableDynamicAuth = false;

    /**
     * scope默认关闭
     */
    private boolean enableDynamicScopeAuth = false;

    /**
     * Scope规则
     */
    private Map<String, List<String>> scopeRule;

    /**
     * 菜单(url)Mapper
     */
    private final MenuMapper menuMapper;

    public UrlRolesFilterHandler(MenuMapper menuMapper) {
        this.menuMapper = menuMapper;
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 动态权限开启状态
        ((FilterInvocation)object).getRequest().setAttribute(EnableDynamicAutEnum.ENABLE_DYNAMIC_AUTH.getName(),enableDynamicAuth);
        // 动态Scope开启状态
        ((FilterInvocation)object).getRequest().setAttribute(EnableDynamicAutEnum.ENABLE_DYNAMIC_SCOPE_AUTH.getName(),enableDynamicScopeAuth);
        // 请求URI
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        ArrayList<String> names = new ArrayList<>();
        // 开启动态路由
        if(enableDynamicAuth){
            List<String> roleNames = menuMapper.selectRoleNamesByUrl(requestUrl);
            names.addAll(roleNames.stream().map(EnableDynamicAutEnum.ENABLE_DYNAMIC_AUTH.getPrefix()::concat).collect(Collectors.toList()));
        }
        // 开启动态路由
        if(enableDynamicScopeAuth){
            List<String> scopeNames = new ArrayList<>();
            if(MapUtils.isEmpty(scopeRule)){
                log.error("作用域规则为空");
                throw new BusinessCommonException(Oauth2ExceptionEnum.SCOPE_EMPTY.getCode(),Oauth2ExceptionEnum.SCOPE_EMPTY.getMsg());
            }
            // url 存在scope
            for (Map.Entry<String,List<String>> entry:scopeRule.entrySet()) {
                if(entry.getValue().contains(requestUrl)){
                    scopeNames.add(entry.getKey());
                }
            }
            names.addAll(scopeNames.stream().map(EnableDynamicAutEnum.ENABLE_DYNAMIC_SCOPE_AUTH.getPrefix()::concat).collect(Collectors.toList()));
        }
        String[] toBeStored = new String[]{};
        return SecurityConfig.createList(names.toArray(toBeStored));
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }

    public boolean isEnableDynamicAuth() {
        return enableDynamicAuth;
    }

    public void setEnableDynamicAuth(boolean enableDynamicAuth) {
        this.enableDynamicAuth = enableDynamicAuth;
    }

    public boolean isEnableDynamicScopeAuth() {
        return enableDynamicScopeAuth;
    }

    public void setEnableDynamicScopeAuth(boolean enableDynamicScopeAuth) {
        this.enableDynamicScopeAuth = enableDynamicScopeAuth;
    }

    public Map<String, List<String>> getScopeRule() {
        return scopeRule;
    }

    public void setScopeRule(Map<String, List<String>> scopeRule) {
        this.scopeRule = scopeRule;
    }

}
